/*
 * Sylvain BERTRAND <digital.ragnarok@gmail.com>
 * code protected by GNU GPL v3
 */
#include <X11/Xlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
#include "../nls.h"
#include "../toolkit/paths.h"
#include "debug.h"
#include "misc.h"
#include "wm.h"
#include "window.h"
static gboolean enabled_types[WM_DEBUG_TYPE_NUM] = { FALSE };
static FILE *log_file = NULL;
static guint rr_handler_id = 0;
static guint t_handler_id = 0;
static guint lboxwm_handler_id = 0;

static inline void log_print(FILE *out, const gchar *log_domain, const gchar *level, const gchar *message)
{
	fprintf(out, "%s", log_domain);
	fprintf(out, "-");
	fprintf(out, "%s", level);
	fprintf(out, ": ");
	fprintf(out, "%s", message);
	fprintf(out, "\n");
	fflush(out);
}

static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer data)
{
	FILE *out;
	const gchar *level;
	(void)data;
	switch (log_level & G_LOG_LEVEL_MASK) {
	case G_LOG_LEVEL_DEBUG:
		level = "Debug";
		out = stdout;
		break;
	case G_LOG_LEVEL_INFO:
		level = "Info";
		out = stdout;
		break;
	case G_LOG_LEVEL_MESSAGE:
		level = "Message";
		out = stdout;
		break;
	case G_LOG_LEVEL_WARNING:
		level = "Warning";
		out = stderr;
		break;
	case G_LOG_LEVEL_CRITICAL:
		level = "Critical";
		out = stderr;
		break;
	case G_LOG_LEVEL_ERROR:
		level = "Error";
		out = stderr;
		break;
	default:
		g_assert_not_reached();
	}
	log_print(out, log_domain, level, message);
	if(log_file)
		log_print(log_file, log_domain, level, message);
}

void wm_debug_startup(void)
{
	struct t_paths *p = t_paths_new();
	gchar *dir = g_build_filename(t_paths_cache_home(p), "lboxwm", NULL);
	if(!t_paths_mkdir_path(dir, 0777))
		g_message(_("Unable to make directory '%s': %s"), dir, g_strerror(errno));
	else {
		gchar *name = g_build_filename(t_paths_cache_home(p), "lboxwm", "lboxwm.log", NULL);
		unlink(name);
		log_file = fopen(name, "w");
		g_free(name);
	} rr_handler_id =
		g_log_set_handler("lboxwm_render", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL);
	t_handler_id =
		g_log_set_handler("lboxwm_toolkit", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL);
	lboxwm_handler_id =
		g_log_set_handler("lboxwm", G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION, log_handler, NULL);
	t_paths_unref(p);
	g_free(dir);
}

void wm_debug_shutdown(void)
{
	g_log_remove_handler("lboxwm_render", rr_handler_id);
	g_log_remove_handler("lboxwm_toolkit", t_handler_id);
	g_log_remove_handler("lboxwm", lboxwm_handler_id);
	if(log_file) {
		fclose(log_file);
		log_file = NULL;
	}
}

void wm_debug_enable(enum wm_debug_t type, gboolean enable)
{
	g_assert(type < WM_DEBUG_TYPE_NUM);
	enabled_types[type] = enable;
}

static inline void log_argv(enum wm_debug_t type, const gchar *format, va_list args)
{
	const gchar *prefix;
	gchar *message;
	g_assert(type < WM_DEBUG_TYPE_NUM);
	if(!enabled_types[type])
		return;
	switch (type) {
	case WM_DEBUG_FOCUS:
		prefix = "(FOCUS) ";
		break;
	case WM_DEBUG_APP_BUGS:
		prefix = "(APPLICATION BUG) ";
		break;
	default:
		prefix = NULL;
		break;
	}
	message = g_strdup_vprintf(format, args);
	if(prefix) {
		gchar *a = message;
		message = g_strconcat(prefix, message, NULL);
		g_free(a);
	}
	g_debug("%s", message);
	g_free(message);
}

void wm_debug(const gchar *a, ...)
{
	va_list vl;
	va_start(vl, a);
	log_argv(WM_DEBUG_NORMAL, a, vl);
	va_end(vl);
}

void wm_debug_type(enum wm_debug_t type, const gchar *a, ...)
{
	va_list vl;
	va_start(vl, a);
	log_argv(type, a, vl);
	va_end(vl);
}
